/**********************************************************************

  Audacity: A Digital Audio Editor

  @file LV2Editor.h
  @brief Event handling object for user interfaces of LV2 effects

  Paul Licameli split from LV2Effect.h

  Audacity(R) is copyright (c) 1999-2013 Audacity Team.
  License: GPL v2 or later.  See License.txt.

*********************************************************************/

#ifndef __AUDACITY_LV2_EDITOR__
#define __AUDACITY_LV2_EDITOR__

#if USE_LV2

#include "../EffectEditor.h"

#include <optional>
#include <vector>

#include <wx/dialog.h>
#include <wx/timer.h>
#include <wx/weakref.h>
#include <wx/window.h>
#include <wx/windowptr.h>

#include "LV2UIFeaturesList.h"
#include "LV2Ports.h"
#include "NativeWindow.h"

using SuilInstancePtr = Lilv_ptr<SuilInstance, suil_instance_free>;

// We use deprecated LV2 interfaces to remain compatible with older
// plug-ins, so disable warnings
LV2_DISABLE_DEPRECATION_WARNINGS

class wxButton;
class wxCheckBox;
class wxChoice;
class wxSlider;
class wxTextCtrl;
class NumericTextCtrl;

class LV2EffectMeter;
class LV2Instance;
class LV2Wrapper;
class StatelessEffectUIServices;

class LV2Editor final : public EffectEditor, public wxEvtHandler, LV2UIFeaturesList::UIHandler
{
public:
    LV2Editor(const StatelessEffectUIServices& effect, EffectType type, const LilvPlugin& plug, LV2Instance& instance,
              EffectSettingsAccess& access, const EffectOutputs* pOutputs, double sampleRate, const LV2FeaturesList& features,
              const LV2Ports& ports, wxWindow* parent, bool useGUI);
    ~LV2Editor() override;

    bool ValidateUI() override;
    bool UpdateUI() override;
    bool IsGraphicalUI() override;
    void Disconnect() override;

    int ui_resize(int width, int height) override;
    void ui_closed() override;

#if defined(__WXGTK__)
    static void size_request(GtkWidget* widget, GtkRequisition* requisition, LV2Editor* pEditor);
    void SizeRequest(GtkWidget* widget, GtkRequisition* requisition);
#endif

    /*!
     @pre `pWrapper != nullptr`
     */
    bool BuildFancy(std::unique_ptr<LV2Wrapper> pWrapper, const EffectSettings& settings);
    bool BuildPlain(EffectSettingsAccess& access);

    void suil_port_write(uint32_t port_index, uint32_t buffer_size, uint32_t protocol, const void* buffer) override;
    uint32_t suil_port_index(const char* port_symbol) override;

    void UpdateControlPortValue(LV2EffectSettings& settings, size_t controlPortIndex, float value);

    void OnTrigger(wxCommandEvent& evt);
    void OnToggle(wxCommandEvent& evt);
    void OnChoice(wxCommandEvent& evt);
    void OnText(wxCommandEvent& evt);
    void OnSlider(wxCommandEvent& evt);

    void OnIdle(wxIdleEvent& evt);
    void OnSize(wxSizeEvent& evt);

    static std::shared_ptr<SuilHost> GetSuilHost();

    const LilvPlugin& mPlug;
    const EffectType mType;
    LV2Instance& mInstance;
    const EffectOutputs* mpOutputs{};
    const double mSampleRate;
    const LV2Ports& mPorts;
    std::unique_ptr<LV2Wrapper> mpWrapper;
    std::optional<const LV2UIFeaturesList> mUIFeatures;
    LV2PortUIStates mPortUIStates;

    std::shared_ptr<SuilHost> mSuilHost;
    wxWindow* mParent;
    bool mUseGUI{};

    // UI
    struct PlainUIControl {
        wxTextCtrl* mText{};
        //! Discriminate this union according to corresponding port's properties
        union {
            wxButton* button;
            wxCheckBox* checkbox;
            wxChoice* choice;
            LV2EffectMeter* meter;
            wxSlider* slider;
        };
    };
    //! Array in correspondence with the control ports
    std::vector<PlainUIControl> mPlainUIControls;
    void SetSlider(const LV2ControlPortState& state, const PlainUIControl& ctrl);

    // Two smart pointers are grouped because their destruction needs caution
    struct UI {
        void Destroy();
        ~UI() { Destroy(); }
        SuilInstancePtr mSuilInstance;
        wxWindowPtr<NativeWindow> mNativeWin{};
#ifdef __WXMAC__
        bool mJustLeakMemory{ false };
#endif
    } mUI;

    wxSize mNativeWinInitialSize{ wxDefaultSize };
    wxSize mNativeWinLastSize{ wxDefaultSize };
    bool mResizing{ false };
#if defined(__WXGTK__)
    bool mResized{ false };
#endif

    wxWeakRef<wxDialog> mDialog;
    bool mExternalUIClosed{ false };

    //! This must be destroyed before mSuilInstance
    struct Timer : wxTimer {
        LV2_External_UI_Widget* mExternalWidget{};
        void Notify() override;
    } mTimer;

    const LV2UI_Idle_Interface* mUIIdleInterface{};
    const LV2UI_Show_Interface* mUIShowInterface{};
    NumericTextCtrl* mDuration{};

    DECLARE_EVENT_TABLE()
};

#endif
#endif
